package com.qkl.user.filter;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qkl.user.config.IgnoreUrlsConfig;
import com.qkl.user.config.JwtConfig;
import com.qkl.user.dto.ExceptionResponse;
import com.qkl.user.enums.ExceptionEnum;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.Key;
@Slf4j
@Component
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {
    @Autowired
    private  JwtConfig jwtConfig;
    @Autowired
    private  UserDetailsService userDetailsService;
    @Autowired
    private IgnoreUrlsConfig ignoreUrlsConfig;
    private static final AntPathMatcher antPathMatcher = new AntPathMatcher();
    @Override
    protected void doFilterInternal(
            HttpServletRequest request,
            HttpServletResponse response,
            FilterChain filterChain
    ) throws ServletException, IOException {
        try {
            // 1. 获取请求头中的令牌
            String authHeader = request.getHeader("Authorization");
            String jwt = null;
            String username = null;
            //String requestPath = exchange.getRequest().getPath().value();
            // 2. 提取令牌（格式：Bearer <token>）
            if (authHeader!= null && authHeader.startsWith("Bearer ")) {
                jwt = authHeader.substring(7);
                // 3. 解析令牌获取用户名
                username = Jwts.parserBuilder()
                        .setSigningKey(getSigningKey())
                        .build()
                        .parseClaimsJws(jwt)
                        .getBody()
                        .getSubject();
            }else {
                Boolean check = false;
                String requestPath = request.getRequestURI();
                for (String pattern : ignoreUrlsConfig.getUrls()) {
                    // 使用 antPathMatcher 进行模式匹配
                    if (antPathMatcher.match(pattern, requestPath)) {
                        // 如果路径匹配到了任一忽略模式，则直接放行
                        check = true;
                        break;
                    }
                }
                if(!check){
                    sendErrorResponse(response, ExceptionEnum.TOKEN_BLACKLISTED);
                    return;
                }
            }
            // 4. 设置认证信息到上下文
            if (username!= null && SecurityContextHolder.getContext().getAuthentication() == null) {
                UserDetails userDetails = userDetailsService.loadUserByUsername(username);
                // 5. 验证令牌有效性
                Jwts.parserBuilder()
                        .setSigningKey(getSigningKey())
                        .build()
                        .parseClaimsJws(jwt);  // 无效会抛出异常

                // 6. 创建认证令牌并设置到上下文
                UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
                        userDetails, null, userDetails.getAuthorities()
                );
                authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(authToken);
            }
            // 继续执行过滤器链
            filterChain.doFilter(request, response);
        } catch (ExpiredJwtException e) {
            sendErrorResponse(response, ExceptionEnum.EXPIRED_TOKEN);
        } catch (JwtException | IllegalArgumentException e) {
            sendErrorResponse(response, ExceptionEnum.INVALID_TOKEN);
        }
    }
    private final ObjectMapper objectMapper = new ObjectMapper();// 用于序列化响应
    // 手动构建错误响应
    private void sendErrorResponse(HttpServletResponse response, ExceptionEnum exceptionEnum) throws IOException {

        // 1. 设置响应状态码和内容类型
        response.setStatus(HttpStatus.UNAUTHORIZED.value());
        response.setContentType("application/json;charset=UTF-8");
        response.setCharacterEncoding(StandardCharsets.UTF_8.name());

        // 2. 构建自定义响应对象
        ExceptionResponse errorResponse = new ExceptionResponse(
                exceptionEnum.getCode(),  // 业务状态码
                exceptionEnum.getMsg(),  // 提示信息
                null  // 数据字段（无数据时为null）
        );

        // 3. 使用 FastJSON 序列化响应对象为JSON字符串
        // 序列化时保留 null 字段
//        String jsonResponse = JSON.toJSONString(
//                errorResponse,
//                SerializerFeature.WriteMapNullValue  // 关键配置：保留 null 字段
//        );
        // 4. 写入响应体
        response.getWriter().write(objectMapper.writeValueAsString(errorResponse));
        //response.getWriter().write(jsonResponse);
    }
    // 获取签名密钥
    private Key getSigningKey() {
        byte[] keyBytes = jwtConfig.getSecret().getBytes(StandardCharsets.UTF_8);
        return Keys.hmacShaKeyFor(keyBytes);
    }
}
